<template>
  <div class="jh-virtual-table virtual-table" :class="{'virtual-table--stripe':stripe,'virtual-table--border':border}">
    <div class="hidden-columns" v-if="false">
      <slot></slot>
    </div>
    <div class="virtual-table-header" ref="tableHeader" v-show="showHeader">
      <div class="virtual-table-tr">
        <template v-for="(item, index) in columns">
          <jh-virtual-table-thead :data="item" cls="virtual-table-th virtual-table-td" trCls='virtual-table-tr' />
        </template>
      </div>
    </div>
    <div class="virtual-table-body" :style="tbodyStyle" ref="tbody"></div>
  </div>
</template>

<script>
import './jh-virtual-scroll';
import { JhPopover } from 'jh-popover';
import FilterOptions from './filter-options.vue';
import JhVirtualTableColumn from './jh-virtual-table-column';
import JhVirtualTableThead from './jh-virtual-table-thead.vue';
export default {
  components: {
    JhPopover,
    FilterOptions,
    JhVirtualTableThead
  },
  props: {
    data: {
      type: Array,
      required: true,
    },
    stripe: {
      type: Boolean,
      default: false,
    },
    border: {
      type: Boolean,
      default: false,
    },
    height: {
      type: Number
    },
    showHeader: {
      type: Boolean,
      default: true
    },
    sortMethod: {
      type: Function,
    },
    rowClassName: {
      type: Function,
    }
  },
  data() {
    return {
      columns: [],
      jhVScroller: null,
      scopedHash: {},
      pagesize: 10,
      pageno: 0,
      checkCls: 'iconfont icon-yk_fuxuan_fill',
      noCheckCls: 'iconfont icon-yduifuxuankuang',
      halfCheckCls: 'iconfont icon-fuxuankuang-weixuanze',
      selected: [],
      sortProp: '',
      sortOrder: '',
      filters: [],
      filterProp: '',
      filterVisible: false,
    };
  },
  computed: {
    selectionCls() {
      let cls = '';
      if (this.maxLength > 0) {
        if (this.selected.length === 0) {
          cls = this.noCheckCls;
        } else if (this.selected.length < this.maxLength) {
          cls = this.halfCheckCls;
        } else {
          cls = this.checkCls;
        }
      }
      return cls;
    },
    maxLength() {
      return this.filterData.length;
    },
    filterData() {
      const list = (this.data || []).filter(o => {
        if (this.filters.length === 0) {
          return true;
        } else {
          return this.filters.includes(o[this.filterProp]);
        }
      });
      if (this.sortProp) {
        list.sort(this.defaultSortMethod);
      }
      return list;
    },
    filterPopoverData() {
      return (this.data || []).map(o => o[this.filterProp]);
    },
    tbodyStyle() {
      const style = {}
      if (this.height) {
        style.height = this.height + 'px';
      }
      return style;
    }
  },
  methods: {
    getData() {
      return this.filterData.slice(
        (++this.pageno - 1) * this.pagesize,
        this.pageno * this.pagesize,
      );
    },
    getSkeleton() {
      var div = document.createElement('div');
      div.textContent = 'loading...';
      const styles = {
        width: '100%',
        lineHeight: '32px',
        boxSizing: 'border-box',
        padding: '0 5px',
        color: 'gray',
        fontSize: '12px',
        cursor: 'wait',
      };
      Object.keys(styles).forEach(key => {
        div.style[key] = styles[key];
      });
      div.title = '请稍候';
      return div;
    },
    toRender(data, rowIndex) {
      const tr = document.createElement('div');
      tr.classList = this.setVirtualRowClass(data, rowIndex).join(' ');
      const vnode = JhVirtualTableColumn.render.call(this, this.$createElement);
      vnode.children = this.adjustChildColumns(vnode, rowIndex);
      this.injectionData(vnode, data, rowIndex);
      const tnode = this.__patch__(document.createElement('div'), vnode);
      for (let i = 0; i < tnode.childNodes.length; i++) {
        if (tnode.childNodes[i].nodeType !== 3) {
          this.judgeSelection(tnode.childNodes[i], i, rowIndex);
          tr.appendChild(tnode.childNodes[i]);
        }
      }
      this.injectionHash(tr);
      return tr;
    },
    setVirtualRowClass(data, rowIndex) {
      const classList = ['virtual-table-tr', rowIndex % 2 === 0 ? 'virtual-table-tr--even' : 'virtual-table-tr--odd'];
      if (this.rowClassName instanceof Function) {
        classList.push(this.rowClassName({ row: data, rowIndex }));
      }
      return classList;
    },
    adjustChildColumns(vnode) {
      const columns = [];
      (vnode.children || []).forEach(column => {
        if (this.isColumnVnode(column)) {
          const children = column.componentOptions.children || [];
          if (children.length === 0) {
            columns.push(column);
          } else {
            let shouldDeep = false;
            column.componentOptions.children.forEach(item => {
              if (this.isColumnVnode(item)) {
                shouldDeep = true;
              }
            })
            if (shouldDeep) {
              columns.push(...this.adjustChildColumns(column.componentOptions))
            }
          }
        } else {
          columns.push(column);
        }
      })
      return columns;
    },
    judgeSelection(node, index, rowIndex) {
      const column = this.columns[index];
      if (column && column.type === 'selection') {
        const selectEl = node.querySelector('i');
        if (selectEl) {
          selectEl.classList = this.selected.includes(rowIndex)
            ? this.checkCls
            : this.noCheckCls;
          selectEl.addEventListener('click', () => {
            const sIndex = this.selected.indexOf(rowIndex);
            if (sIndex > -1) {
              selectEl.classList = this.noCheckCls;
              this.selected.splice(sIndex, 1);
              this.$emit('selection-change', [rowIndex], false);
            } else {
              selectEl.classList = this.checkCls;
              this.selected.push(rowIndex);
              this.$emit('selection-change', [rowIndex], true);
            }
          });
        }
      }
    },
    injectionData(vnode, data, rowIndex) {
      if (vnode.tag) {
        vnode.data['selfData'] = data;
        vnode.data['selfIndex'] = rowIndex;
        (vnode.children || []).forEach(child => {
          this.injectionData(child, data, rowIndex);
        });
      }
    },
    injectionHash(node) {
      if (
        this.scopedHash !== null &&
        Object.keys(this.scopedHash).length === 0
      ) {
        Object.keys(this.$refs.tbody.dataset).forEach(key => {
          this.$set(this.scopedHash, key, '');
        });
        if (Object.keys(this.scopedHash).length === 0) {
          this.scopedHash = null;
        }
      }
      Object.keys(this.scopedHash || {}).forEach(hash => {
        node.dataset[hash] = '';
        if (node.childNodes.length > 0) {
          node.childNodes.forEach(child => {
            if (child.nodeType === 1) {
              this.injectionHash(child);
            }
          });
        }
      });
    },
    handleHeadSelection() {
      let cls = '';
      if (this.selected < this.maxLength) {
        cls = this.checkCls;
        this.selected = [...Array(this.maxLength).keys()].map(i => i);
        this.$emit('selection-change', [...this.selected], true);
      } else {
        this.selected = [];
        cls = this.noCheckCls;
        this.$emit('selection-change', [], false);
      }
      if (this.jhVScroller) {
        this.columns.forEach((column, index) => {
          if (column.type === 'selection') {
            this.jhVScroller.el.childNodes.forEach(tr => {
              if (tr.childNodes[index]) {
                const selectEl = tr.childNodes[index].querySelector('i');
                if (selectEl) {
                  selectEl.classList = cls;
                }
              }
            });
          }
        });
      }
    },
    handleSortClick(prop) {
      if (this.sortProp !== prop) {
        this.sortProp = prop;
        this.sortOrder = 'ascending';
      } else {
        this.sortOrder =
          this.sortOrder === 'ascending' ? 'descending' : 'ascending';
      }
    },
    defaultSortMethod(a, b) {
      const sortFun = (a, b) => {
        if (typeof a === 'string') {
          return a.localeCompare(b);
        } else if (typeof a === 'number') {
          return a - b;
        } else if (typeof a === 'boolean') {
          return a && b;
        } else {
          if (a instanceof Date && b instanceof Date) {
            return a.getTime() - b.getTime();
          } else {
            return (a || '').toString().localeCompare((b || '').toString());
          }
        }
      };
      if (this.sortMethod) {
        this.sortMethod(a, b, this.sortProp, this.sortOrder);
      } else {
        if (this.sortOrder === 'ascending') {
          return sortFun(a[this.sortProp], b[this.sortProp]);
        } else {
          return sortFun(b[this.sortProp], a[this.sortProp]);
        }
      }
    },
    handleFTgChange(isshow, prop) {
      this.filterProp = prop;
      this.$refs.filterTrigger.forEach(item => {
        if (item.$el.dataset.prop !== prop) {
          item.close();
        }
      });
    },
    handleFOpchange(list, prop) {
      console.log('筛选有问题，先做其他功能');
    },
    autoHeaderBorder(vnode) {
      if (this.jhVScroller) {
        if (this.height && this.jhVScroller.el.scrollHeight > this.jhVScroller.el.clientHeight) {
          this.$refs.tableHeader.style.borderRightWidth = 6;
        } else {
          this.$refs.tableHeader.style.borderRightWidth = 0;
        }
      }
    },
    fullRender() {
      const table = document.createElement('table');
      const thead = document.createElement('thead');
      const tbody = document.createElement('tbody');
      this.$refs.tableHeader.childNodes.forEach(head => {
        const tr = document.createElement('tr');
        head.childNodes.forEach(div => {
          const th = document.createElement('th');
          th.innerHTML = div.innerHTML;
          tr.appendChild(th);
        });
        thead.appendChild(tr);
      });
      table.appendChild(thead);
      this.filterData.forEach((data, index) => {
        const rawDom = this.toRender(data, index);
        const tr = document.createElement('tr');
        rawDom.childNodes.forEach(div => {
          const td = document.createElement('td');
          td.innerHTML = div.innerHTML;
          tr.appendChild(td);
        });
        tbody.appendChild(tr);
      });
      table.appendChild(tbody);
      return table;
    },
    isColumnVnode(vnode) {
      return (vnode.tag || '').includes('jh-virtual-table-column')
    }
  },
  watch: {
    '$slots.default': {
      handler(newName, oldName) {
        const ths = [];
        const forEachColumn = vnodes => {
          const results = [];
          (vnodes || []).forEach(vnode => {
            if (this.isColumnVnode(vnode)) {
              const item = vnode.componentOptions.propsData;
              item.children = [];
              if ('sortable' in item && item.sortable === '') {
                item.sortable = true;
              }
              if ('filterable' in item && item.filterable === '') {
                item.filterable = true;
              }
              item.children.push(...forEachColumn(vnode.componentOptions.children));
              results.push(item);
            }
          });
          return results;
        }
        this.columns = forEachColumn(this.$slots.default || []);
      },
      immediate: true,
    },
    filterData: {
      handler(newValue, oldValue) {
        if (newValue.length > 0) {
          if (this.jhVScroller) {
            this.jhVScroller.onReload(() => {
              this.pageno = 0;
            });
          } else {
            this.jhVScroller = new window.JhVirtualScroll(this.$refs.tbody, {
              fetch: this.getData,
              skeleton: this.getSkeleton,
              render: this.toRender,
              afterRender: this.autoHeaderBorder
            }, 10, this.height ? this.$refs.tbody : this.$parent.$el);
          }
        }
      },
      immediate: true,
    },
  },
};
</script>

<style lang="less" scoped>
@borderColor: #ebeef5;
.jh-virtual-table {
  color: #606266;
  position: relative;
  line-height: normal;
  &.virtual-table--stripe {
    .virtual-table-tr--odd {
      background: #f2f2f2;
    }
  }
  &.virtual-table--border {
    .virtual-table-header {
      border-top: 1px solid transparent;
      border-left: 1px solid @borderColor;
    }
    .virtual-table-body {
      .virtual-table-tr {
        border-left: 1px solid @borderColor;
        &:nth-child(1) {
          border-top: 1px solid @borderColor;
        }
      }
    }
    .virtual-table-td {
      border-right: 1px solid @borderColor;
    }
    .virtual-table-th {
      border-top: 1px solid @borderColor;
    }
  }
  .virtual-table-header {
    box-sizing: border-box;
    position: relative;
    border-right: 6px solid transparent;
  }
  .virtual-table-body {
    box-sizing: border-box;
    position: relative;
    &::-webkit-scrollbar {
      width: 6px;
    }
    &::-webkit-scrollbar-thumb {
      border-radius: 6px;
      background: rgba(144, 146, 152, 0.5);
    }
    &::-webkit-scrollbar-track {
      border-radius: 6px;
      background: transparent;
    }
  }
  .virtual-table-tr {
    display: flex;
    box-sizing: inherit;
    width: 100%;
  }
  .virtual-table-th {
    font-weight: 700;
    text-align: center;
    background: #f5f7fa;
    // color: #909399;
    .caret-wrapper {
      display: inline-flex;
      flex-direction: column;
      align-items: center;
      height: 34px;
      width: 24px;
      vertical-align: middle;
      // cursor: pointer;
      overflow: initial;
      position: relative;
      .sort-caret {
        width: 0;
        height: 0;
        border: 5px solid transparent;
        position: absolute;
        left: 7px;
        &.ascending {
          border-bottom-color: rgba(255, 255, 255, 0.6);
          top: 5px;
          &.current {
            border-bottom-color: #fff;
          }
        }
        &.descending {
          border-top-color: rgba(255, 255, 255, 0.6);
          bottom: 7px;
          &.current {
            border-top-color: #fff;
          }
        }
      }
    }
    .filter-trigger {
      display: inline-block;
      .handler {
        .iconfont {
          color: rgba(255, 255, 255, 0.6);
          &.current {
            color: #fff;
          }
        }
      }
    }
  }
  .virtual-table-td {
    box-sizing: inherit;
    padding: 3px 0 4px;
    flex: 1;
    text-align: center;
    border-bottom: 1px solid @borderColor;
  }
}
</style>
